﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EyFramework.Common;
using Newtonsoft.Json;
using StackExchange.Redis;

namespace EyFramework.ThirdPart.RedisUtil
{
    public class RedisCache : ICache
    {
        private int Default_Timeout = 600;//默认超时时间（单位秒）
        private string redis_IP;
        private static string redis_Port;
        private static string redis_Password;
        private JsonSerializerSettings jsonConfig = new JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore, NullValueHandling = NullValueHandling.Ignore };
        private ConnectionMultiplexer connectionMultiplexer;
        private IDatabase database;

        private class CacheObject<T>
        {
            public int ExpireTime { get; set; }
            public bool ForceOutofDate { get; set; }
            public T Value { get; set; }
        }

        public RedisCache()
        {
            this.redis_IP = Globals.GetSysConfig("redis_ip", "192.168.0.161"); ;
            redis_Port = Globals.GetSysConfig("redis_Port", "6379");
            redis_Password = Globals.GetSysConfig("redis_Password", "");
            ConfigurationOptions configOptions = new ConfigurationOptions
            {
                EndPoints = {
                    { redis_IP,int.Parse(redis_Port) }
                },
                KeepAlive = 180,      //发送信息以保持sockets在线的间隔时间
                Password = redis_Password,   //密码
                DefaultVersion = new Version("2.8.5"),   //Redis版本
                AllowAdmin = true     //启用被认定为是有风险的一些命令
            };
            if (this.redis_IP == null || string.IsNullOrWhiteSpace(this.redis_IP.ToString()))
                throw new ApplicationException("配置文件中未找到redis_ip的有效配置");
            connectionMultiplexer = ConnectionMultiplexer.Connect(configOptions);
            database = connectionMultiplexer.GetDatabase();
        }

        /// <summary>
        /// 连接超时设置
        /// </summary>
        public int TimeOut
        {
            get
            {
                return Default_Timeout;
            }
            set
            {
                Default_Timeout = value;
            }
        }

        /// <summary>
        /// 消息订阅
        /// </summary>
        /// <param name="passageway">主题标志</param>
        /// <param name="handler">收到消息回调</param>
        public void Subscribe(string sChannel, Action<string> sHandler)
        {
            ISubscriber sub = connectionMultiplexer.GetSubscriber();
            sub.Subscribe(sChannel, (channel, message) =>
            {
                sHandler.Invoke(message);
            });
        }

        /// <summary>
        /// 消息发布
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="message"></param>
        public void Publish(string channel, string message)
        {
            ISubscriber sub = connectionMultiplexer.GetSubscriber();
            sub.Publish(channel, message);
        }

        public object Get(string key)
        {
            return Get<object>(key);
        }

        public T Get<T>(string key)
        {
            DateTime begin = DateTime.Now;
            var cacheValue = database.StringGet(key);
            DateTime endCache = DateTime.Now;
            var value = default(T);
            if (!cacheValue.IsNull)
            {
                var cacheObject = JsonConvert.DeserializeObject<CacheObject<T>>(cacheValue, jsonConfig);
                if (!cacheObject.ForceOutofDate)
                    database.KeyExpire(key, new TimeSpan(0, 0, cacheObject.ExpireTime));
                value = cacheObject.Value;
            }
            DateTime endJson = DateTime.Now;
            return value;
        }

        public void Insert(string key, object data)
        {
            var jsonData = GetJsonData(data, TimeOut, false);
            database.StringSet(key, jsonData);
        }

        public void Insert(string key, object data, int cacheTime)
        {
            var timeSpan = TimeSpan.FromSeconds(cacheTime);
            var jsonData = GetJsonData(data, TimeOut, true);
            database.StringSet(key, jsonData, timeSpan);
        }

        public void Insert(string key, object data, DateTime cacheTime)
        {
            var timeSpan = cacheTime - DateTime.Now;
            var jsonData = GetJsonData(data, TimeOut, true);
            database.StringSet(key, jsonData, timeSpan);
        }

        public void Insert<T>(string key, T data)
        {
            var jsonData = GetJsonData<T>(data, TimeOut, false);
            database.StringSet(key, jsonData);
        }

        public void Insert<T>(string key, T data, int cacheTime)
        {
            var timeSpan = TimeSpan.FromSeconds(cacheTime);
            var jsonData = GetJsonData<T>(data, TimeOut, true);
            database.StringSet(key, jsonData, timeSpan);
        }

        public void Insert<T>(string key, T data, DateTime cacheTime)
        {
            var timeSpan = cacheTime - DateTime.Now;
            var jsonData = GetJsonData<T>(data, TimeOut, true);
            database.StringSet(key, jsonData, timeSpan);
        }

        private string GetJsonData(object data, int cacheTime, bool forceOutOfDate)
        {
            var cacheObject = new CacheObject<object>() { Value = data, ExpireTime = cacheTime, ForceOutofDate = forceOutOfDate };
            return JsonConvert.SerializeObject(cacheObject, jsonConfig);//序列化对象
        }

        private string GetJsonData<T>(T data, int cacheTime, bool forceOutOfDate)
        {
            var cacheObject = new CacheObject<T>() { Value = data, ExpireTime = cacheTime, ForceOutofDate = forceOutOfDate };
            return JsonConvert.SerializeObject(cacheObject, jsonConfig);//序列化对象
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="key"></param>
        public void Remove(string key)
        {
            database.KeyDelete(key, CommandFlags.HighPriority);
        }

        /// <summary>
        /// 判断key是否存在
        /// </summary>
        public bool Exists(string key)
        {
            return database.KeyExists(key);
        }
    }
}